# -*- coding:utf-8 -*-

import os
import sys
import shutil
import time
import thread
import threading
import zipfile
import glob
import random
import  signal
from hashlib import md5

from MyException import Error
from MyException import GetSizeError
from MyException import NotFileError
from MyException import BigFileError
from MyException import BigZipError
from MyException import NonZipError
from MyException import CopyFileError
from MyLogger import MyLogger, SIZEROTATE, TIMEROTATE
from StatLog import StatLog
from ConfigInfo import Profile
import GlobalVar as GlobalVar

class MyReceive(threading.Thread):
    def __init__(self, profile,type, mutex, num):
        """
        初始化线程
        :param profile: 进程的所有配置
        :param type: 接收类型
        :param fsinfo: 磁盘状态
        :param file_list: 锁文件
        """
        threading.Thread.__init__(self)
        self.type = type
        self.profile = profile
        self.work_dir = ""
        self.suffix = ""
        self.size_threshold = 0
        self.num_thresold = 0
        self.mutex = mutex
        self.num = num

        if (type == "Bigdata"):
            self.work_dir = profile.recv_bigdata
            self.suffix = "*.nb"
            self.max_time = profile.recv_bigdata_max_time
            self.size_threshold = profile.recv_bigdata_max_size
        elif (type == "Keyword"):
            self.work_dir = profile.recv_keyword
            self.suffix = "*.bcp"
            self.max_time = profile.recv_keyword_max_time
            self.size_threshold = profile.recv_keyword_max_size
        elif (type == "Mass"):
            self.work_dir = profile.recv_mass
            self.suffix = "*.bcp"
            self.max_time = profile.recv_mass_max_time
            self.size_threshold = profile.recv_mass_max_size

        self.run_logger = MyLogger(os.path.join(profile.log_dir, "zip_" + self.type + ".log"), SIZEROTATE, profile.size_rotate_backup_num)
        self.run_logger.set_log_lev(profile.log_lev)

    def run(self):
        """
        业务函数
        :return: 
        """
        if(self.work_dir == ""):
            self.run_logger.error("invalid work dir, int Recv %s Thread"%self.type)
            return

        self.run_logger.info("[ZIP %s Thread %s] started, Recv Dir : %s Send dir ; %s" % (self.type, str(self.num), self.work_dir, self.profile.send_zip))
        temp_dir = os.path.join(self.work_dir, "Temp")

        self.run_logger.debug("[ZIP %s Thread %s] runflag : %s"%(self.type, self.num, str(GlobalVar.get_run_flag())))

        while GlobalVar.get_run_flag():
            time.sleep(1)

            # 获得锁
            # self.run_logger.debug("[ZIP %s Thread %s] acquire mutex" % (self.type, str(self.num)))
            self.mutex.acquire()
            # self.run_logger.debug("[ZIP %s Thread %s] acquire mutex sucess" % (self.type, str(self.num)))

            #获取接受目录中文件
            file_list = glob.glob('%s/%s'%(self.work_dir,self.suffix))

            # 判断文件个数或文件总大小有没有达到阈值
            flag = self.is_touch_threshold(file_list)
            if flag == False :
                # continue之前要释放锁
                self.mutex.release()
                # self.run_logger.debug("[ZIP %s Thread %s] files total count and size not touch threshold" % (self.type, str(self.num)))
                continue

            # mv待压缩的文件到临时目录
            # 临时目录名格式 : type_timestamp_random
            # 例如：Bigdata_1495104324_12
            zip_dir_name = self.type + "_" + str(int(time.time())) + "_" + str(random.randint(0, 99))
            zip_dir_path = os.path.join(self.work_dir, zip_dir_name)

            try:
                # 创建临时压缩目录
                if not os.path.exists(zip_dir_path):
                    os.makedirs(zip_dir_path)
                    self.run_logger.info("[ZIP %s Thread %s] mkdir sucess, path: %s" % (self.type, str(self.num), zip_dir_path))
            except:
                self.run_logger.error("[ZIP %s Thread %s] mkdir failed, path: %s" % (self.type, str(self.num), zip_dir_path))
                #os.kill(os.getpid(), signal.SIGTERM)
                self.mutex.release()
                continue

            try:
                # 将待压缩文件拷贝到临时目录
                for filepath in file_list:
                    try:
                        filename = os.path.split(filepath)[1]
                        dst_file_path = os.path.join(zip_dir_path, filename)
                        tmp_file_path = dst_file_path + ".tmp"
                        # 拷贝文件到临时目录
                        shutil.copyfile(filepath, tmp_file_path)
                        self.run_logger.info("[ZIP %s Thread %s] copy to tmp file success, %s" %(self.type, str(self.num), tmp_file_path))
                        # 改回原名
                        os.rename(tmp_file_path, dst_file_path)
                        self.run_logger.info("[ZIP %s Thread %s] rename file sucess, %s" %(self.type, str(self.num), dst_file_path))
                        # 删除原文件
                        os.remove(filepath)
                        self.run_logger.debug("[ZIP %s Thread %s] remove old file sucess, %s" %(self.type, str(self.num), filepath))
                    except:
                        self.run_logger.error("[ZIP %s Thread %s] copy file %s failed" % (self.type, str(self.num), filepath))
                        # 把出错的文件转移到backup目录
                        backup_dir = os.path.join(os.path.join(self.work_dir, self.type), time.strftime("%Y%m%d", time.localtime()))
                        shutil.move(filepath, backup_dir)
                        self.run_logger.info("[ZIP %s Thread %s] move %s to backup dir %s" %(self.type, str(self.num), filepath, backup_dir))
                        raise
            except:
                #os.kill(os.getpid(), signal.SIGTERM)
                # 出现异常，删除临时目录
                shutil.rmtree(zip_dir_path)
                self.run_logger.info("[ZIP %s Thread %s] remove tmp zip dir %s" %(self.type, str(self.num), zip_dir_path))
                self.mutex.release()
                continue

            # 释放锁
            self.mutex.release()
            # self.run_logger.debug("[ZIP %s Thread %s] release mutex sucess" % (self.type, str(self.num)))

            # 计算每个文件的md5，加入到md5list文件中
            zip_file_list = os.listdir(zip_dir_path)
            md5list_file = os.path.join(zip_dir_path, "md5list")
            md5list_file_obj = open(md5list_file, "w")
            try:
                for zip_file in zip_file_list:
                    try:
                        path = os.path.join(zip_dir_path, zip_file)
                        md5str = str(self.md5_for_file(path))
                        self.run_logger.debug("[ZIP %s Thread %s] generate md5 sucess"%(self.type, self.num))
                        md5list_file_obj.write(md5str + " " + zip_file + "\n")
                        self.run_logger.debug("[ZIP %s Thread %s] write md5 to md5list sucess"%(self.type, self.num))
                    except:
                        self.run_logger.error("[ZIP %s Thread %s] fail to write md5sum of %s" %(self.type, self.num, zip_file))
                        raise
            except:
                #os.kill(os.getpid(), signal.SIGTERM)
                md5list_file_obj.close()
                shutil.rmtree(zip_dir_path)
                continue

            md5list_file_obj.close()

            # 拼接zip文件名
            zip_file_name = zip_dir_name + ".zip"

            # 压缩目录中的所有文件
            zip_file_path = self.zip_dir(zip_dir_path, self.work_dir, zip_file_name)
            self.run_logger.info("[ZIP %s Thread %s] zip files done, path : %s"%(self.type, self.num, zip_file_path))

            # 移动zip文件到发送目录
            tmp_dst_zip_path = os.path.join(self.profile.send_zip, zip_file_name + ".temp")
            dst_zip_path = os.path.join(self.profile.send_zip, zip_file_name)
            shutil.copyfile(zip_file_path, tmp_dst_zip_path)
            os.rename(tmp_dst_zip_path, dst_zip_path)
            self.run_logger.info("[ZIP %s Thread %s] move zip file to send dir done. path : %s" % (self.type, str(self.num), dst_zip_path))

            # 删除源文件和zip目录下所有文件
            shutil.rmtree(zip_dir_path)
            os.remove(zip_file_path)


    def is_touch_threshold(self, file_list):
        #if len(file_list) >= self.num_thresold :
        #   return True

        time_stamps = []
        total_size = 0
        try:
            for file in file_list:
                try:
                    # 判断是否是文件
                    if not os.path.isfile(file):
                        shutil.rmtree(file)
                        raise NotFileError(file + "is not an existing regular file")
                    # 获取文件大小
                    try:
                        filesize = os.path.getsize(file)
                    except os.error, msg:
                        raise GetSizeError(msg)

                    total_size += filesize

                    # 获取文件时间戳
                    ts = os.stat(file).st_mtime
                    time_stamps.append(int(ts))

                except Exception, msg:
                    self.run_logger.error("[ZIP %s Thread %s] Error receive file: %s size: %d cause: NotFile" % (self.type, str(self.num), file, filesize))
                    raise
                except GetSizeError, msg:
                    self.run_logger.error("[ZIP %s Thread %s] Error receive file: %s size: %d cause: GetSize" % (self.type, str(self.num), file, filesize))
                    raise
        except Exception, e:
            self.run_logger.error("[ZIP %s Thread %s] occur some exception" % self.type)
            #os.kill(os.getpid(), signal.SIGTERM)
            return False
        else:
            #self.run_logger.debug("[ZIP %s Thread %s] file count %s, total size %s" % (self.type, str(self.num),] str(len(file_list)), str(total_size)))
            self.run_logger.debug("[ZIP %s Thread %s] total file size : %s Kb"%(self.type, self.num, str(float(total_size)/1024)))
            if total_size >= self.size_threshold * 1024:
                #print "total_size:", total_size
                return True

            if len(time_stamps) == 0:
                return False

            # 对时间戳排序，默认升序
            time_stamps.sort()
            c_time = int(time.time())
            self.run_logger.debug("[ZIP %s Thread %s] oldest file's timestamp %s, current timestamp %s"%(self.type, self.num, str(time_stamps[0]), str(c_time)))
            # 如果最老的文件是max_time秒之前的，也满足条件，返回True
            if c_time >= time_stamps[0] + self.max_time :
                return True

            return False

    def md5_for_file(self, file):
        m = md5()
        f = open(file, 'rb')
        buffer = 8192

        while True:
            chunk = f.read(buffer)
            if not chunk : break
            m.update(chunk)

        f.close()
        return m.hexdigest()

    def zip_dir(self, input_dir, output_dir, output_name):
        # 拼接输出文件路径
        output_path = os.path.join(output_dir, output_name)
        # 打开zip
        f = zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED)
        # 获得输入目录下所有文件并压缩
        zip_file_list = os.listdir(input_dir)
        for file in zip_file_list:
            try:
                path = os.path.join(input_dir, file)
                f.write(path, file)
            except Exception, e:
                self.run_logger.error("[ZIP %s Thread %s] fail to zip file %s"%(self.type, self.num, file))
                self.run_logger.error("[ZIP %s Thread %s] %s"%(self.type, self.num, repr(e)))
                #os.kill(os.getpid(), signal.SIGTERM)
        f.close()
        return output_path